﻿using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Workflow;
using System.IO;
using System.Xml.Serialization;
using System.Text;
using Microsoft.SharePoint.Utilities;


public partial class UpdateContentTypeAssociation : System.Web.UI.Page
{
    #region Portal and Site related variables

    private SPWeb _myTeamSite;              // Represents my SubSite or Teamsite in my case (e.g., http://localhost/demosite1)

    #endregion

    #region Paramater variables received from the Workflow Attachment screen

    string _paramWorkflowName = string.Empty;               // The unique name given for this instance of the workflow, remember you can
    // have more than one copy of a workflow on a list, but they must have unique 
    // names

    string _paramWorkflowGuid = string.Empty;               // The definition of the workflow
    string _paramSPListGuid = string.Empty;                 // The GUID of the List being attached to
    string _paramTaskList = string.Empty;                   // The GUID of the task List that the user would like to attach the WF to, or if its
    // a new list to be created, SharePoint will give you your Task List name with a "z"
    // at the begining. Example: zMyNewTaskList

    string _paramHistoryList = string.Empty;                // The GUID of the History List that the user would like to attach the WF to, or its
    // a new list to be created, SharePoint will give you your Task List name with a "z"
    // at the begining. Example: zMyNewHistoryList

    bool _paramAutoStartWF_OnItemCreate;                    // Automatically start WF when List Items are created
    bool _paramAutoStartWF_OnItemChange;                    // Automatically start WF on List Item changes
    bool _paramAllowManualStart;                            // Allow the Workflow to be manually started by users
    string _paramWorkflowAssociationGuid = string.Empty;    // Guid of an already associated (to this list) Workflow

    #endregion

    #region Workflow List, Template and Association Variables

    bool _worfkflowAssociationExists = false;           // Boolean to let me know if I have an association for this workflow already
    SPList _sharePointListAttachedTo;                   // Represents the SharePoint List/Document Library that we are attempting to
    // attach our workflow template to
    Guid _workflowAssociationGuid;                      // Represents the ID of our Workflow Association Object
    SPWorkflowAssociation _workflowAssociationObject;   // Represent the Workflow Association Object, which is the relationship between
    // our workflow and its connections to a specific (list, libraries, etc.)
    Guid _workflowTemplateGuid;                         // Represents the ID to the workflow attemplate that we are attempting to associate
    // with the SharePoint List/Document Library
    SPWorkflowTemplate _workflowTemplateObject;         // Represents the Workflow template (the Workflow.xml file) associated 
    // with a list and the basic information about the workflow, including
    // it's association form(s), initation(s) form, etc. 
    #endregion

    #region Task List and History List variables

    SPList _workflowtTaskList;                          // Represents the Workflow Task List that we will associate with our Workflow Template
    SPList _workflowHistoryList;                        // Represents the Workflow History List that we will associate with our Workflow Template

    #endregion

    protected string m_workflowName;

    // Note: The OnPreInit page is called at the beginning of the Page Initialization process.
    //       This is where we will put a little code, to see if we are in the "Design" or "Production"
    //       mode, and where we will programatically set our Master.Page reference, to get past some SharePoint "design issues."
    protected override void OnPreInit(EventArgs e)
    {
        base.OnPreInit(e);  // This was already here, and we'll leave it

        // This means that this code is not being accessed by the Design-Mode page.  So, I will set my 
        // master page to the same master page as the rest of the site, so that I can maintain the same
        // look and feel.  This is optional, as you could creat your own Master Page and look and feel.

        this._myTeamSite = SPControl.GetContextWeb(Context);
        //this.MasterPageFile = _myTeamSite.MasterUrl;    // Code to set my master page to the sites master page
        this.MasterPageFile = "/_layouts/application.master";
        // I could use "/_layouts/application.master" to use the
        // application.master page instead
    }

    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);

        // Get a list of all of the SharePoint List in the Team Site that we are in.
        SPListCollection listsInSubSite = _myTeamSite.Lists;
    }

    // Method to capture and save the paramters passed to my Association Form from the workflow attachment screen
    private void getWorkflowAttachmentFormParameters()
    {
        this._paramWorkflowName = Request.Params["WorkflowName"];
        this._paramWorkflowGuid = Request.Params["WorkflowDefinition"];
        this._paramSPListGuid = Request.Params["List"];
        this._paramHistoryList = Request.Params["HistoryList"];
        this._paramTaskList = Request.Params["TaskList"];

        this._paramWorkflowAssociationGuid = Request.Params["GuidAssoc"];

        if (Request.Params["AutoStartCreate"] == "ON") // Values = ON or NULL
            this._paramAutoStartWF_OnItemCreate = true;
        if (Request.Params["AutoStartChange"] == "ON") // Values = ON or NULL
            this._paramAutoStartWF_OnItemChange = true;
        if (Request.Params["AllowManual"] == "ON") // Values = ON or NULL
            this._paramAllowManualStart = true;
    }

    // Method to set our Workflow List and Template related values
    private void setWorkflowListandTemplateValues()
    {
        // Get the SharePoint List that the workflow will be associated with
        // by converting the String to a Guid
        if (this._paramSPListGuid != string.Empty)
        {
            this._sharePointListAttachedTo =
                this._myTeamSite.Lists[new Guid(this._paramSPListGuid)];
        }

        // Check to see if there is an existing association between this template and list
        // and if so, let's assign that Guid to our WorkflowAssociationGuid
        if (!string.IsNullOrEmpty(_paramWorkflowAssociationGuid))
        {
            this._worfkflowAssociationExists = true;
            this._workflowAssociationGuid = new Guid(this._paramWorkflowAssociationGuid);
            this._workflowAssociationObject = _sharePointListAttachedTo.WorkflowAssociations[this._workflowAssociationGuid];
        }

        // Setting the Workflow Template variable for the workflow that we will be attaching to the SharePoint List
        this._workflowTemplateGuid = new Guid(this._paramWorkflowGuid);     // Turn the string that was passed in as a param
        // into a real GUID
        this._workflowTemplateObject = this._myTeamSite.WorkflowTemplates[this._workflowTemplateGuid]; // Use the Guid to get the Template
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        // Get our Workflow Parameters
        getWorkflowAttachmentFormParameters();

        // Set the workflow list and template related values, now that we have the Parameter values to work from
        setWorkflowListandTemplateValues();

        m_workflowName = Request.Params["WorkflowName"];
    }

    // Create our Workflow's Task List 
    private void SetWorkflowTaskList()
    {
        if (this._paramTaskList.StartsWith("z")) // if it starts with a "z" that means that it has to be created
        // it's a SharePoint "thing".
        {
            // We may have to create a new list, so lets set the name & Description for our new list
            string taskListName = this._paramWorkflowName + "_taskList";
            string taskListDescription = string.Format("Task list for the {0} workflow.", this._paramWorkflowName);

            // Check to see if a list with the same name as our new list already exists
            bool listExists = false;
            SPListCollection myLists = this._myTeamSite.Lists;
            foreach (SPList list in myLists)
            {
                if (list.Title == taskListName)
                {
                    // if we find a match then exit the list, because we cannot have duplicates
                    listExists = true;
                    break;
                }
            }
            // If a list with this name alread exists, then we will just use it.  You could also choose to throw
            // an error if you wanted the user to create a unique list for this association
            if (listExists == true)
            {
                this._workflowtTaskList = this._myTeamSite.Lists[taskListName];
            }
            else    // This means that the user wants to create a list, AND the list name that we are about to create
            // doesn't already exist, so we can safely create the new list
            {
                // Add the new list to the site and return the GUID for the new list back to us
                Guid newTaskListGuid = this._myTeamSite.Lists.Add(taskListName, taskListDescription, SPListTemplateType.Tasks);

                // Set our Workflow Task list to our newly created list, using the GUID it gave us on creation
                this._workflowtTaskList = this._sharePointListAttachedTo.Lists[newTaskListGuid];
            }
        }
        else // This means that they've chosen NOT to create a list and is planning on using one that already exists
        {
            // The history list already exists, so we will just use it, after we convert the name from string to GUID
            this._workflowtTaskList = this._myTeamSite.Lists[new Guid(this._paramTaskList)];
        }
    }

    // Create our Workflow's Task List 
    private void SetWorkflowHistoryList()
    {
        if (this._paramHistoryList.StartsWith("z")) // if it starts with a "z" that means that it has to be created
        // it's a SharePoint "thing".
        {
            // We may have to create a new list, so lets set the name & Description for our new list
            string historyListName = this._paramWorkflowName + "_historyList";
            string historyListDescription = string.Format("History list for the {0} workflow.", this._paramWorkflowName);

            // Check to see if a list with the same name as our new list already exists
            SPListCollection myList = this._myTeamSite.Lists;
            bool listExists = false;
            foreach (SPList list in myList)
            {
                if (list.Title == historyListName)
                {
                    // if we find a match then exit the list, because we cannot have duplicates
                    listExists = true;
                    break;
                }
            }
            // If a list with this name alread exists, then we will just use it.  You could also choose to throw
            // an error if you wanted the user to create a unique list for this association
            if (listExists == true)
            {
                // See if we can assign our Workflow History List to one with the same name
                // if so, it exists, if not then we'll have to create it.
                this._workflowHistoryList = this._myTeamSite.Lists[historyListName];
            }
            else    // This means that the user wants to create a list, AND the list name that we are about to create
            // doesn't already exist, so we can safely create the new list
            {
                // Add the new list to the site and return the GUID for the new list back to us
                Guid newHistoryListGuid = this._myTeamSite.Lists.Add(historyListName, historyListDescription, SPListTemplateType.WorkflowHistory);

                /// Set our Workflow History list to our newly created list, using the GUID it gave us on creation
                this._workflowHistoryList = this._sharePointListAttachedTo.Lists[newHistoryListGuid];
            }
        }
        else // This means that they've chosen NOT to create a list and is planning on using one that already exists
        {
            // The history list already exists, so we will just use it, after we convert the name from string to GUID
            this._workflowHistoryList = this._myTeamSite.Lists[new Guid(this._paramHistoryList)];
        }
    }

    // This method converts our form data to XML to be passed to the Workflow Project
    public string serializeAssociationFormDataToXML()
    {
        // Serializing the form to a string to be passed to the WF
        // Note: AssociationData is a class that I've created to hold my data in a structured
        //       Serializable class.
        UpdateContentTypeAssociationData mydata = new UpdateContentTypeAssociationData();
        string serializedDataToReturn;  // This local variable will represent the XML version of our data

        // Setting the values of the "myData" class to the values in the forms controls
        mydata.urlTFSWS = txtURLTFSWS.Text;
        mydata.errorEmailTo = txtEmail.Text;

        // Serialize the data class to XML, because Workflows only receive data that has been
        // serialized as XML.
        using (MemoryStream stream = new MemoryStream())
        {
            XmlSerializer serializer = new XmlSerializer(typeof(UpdateContentTypeAssociationData));
            serializer.Serialize(stream, mydata);
            stream.Position = 0;
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);

            serializedDataToReturn = Encoding.UTF8.GetString(bytes);   // Serializing the form data to XML, to send to the workflow
        }

        // return the XML Serialized string
        return serializedDataToReturn;
    }

    // This method sets the values that the Workflow Association needs before we save the association to SharePoint
    // between the SharePoint List and the Workflow Template
    private void setWorkflowToListAssociationValues()
    {
        // Set the Workflow Association Objects values that match with our parameters
        this._workflowAssociationObject.Name = this._paramWorkflowName;
        this._workflowAssociationObject.AutoStartChange = this._paramAutoStartWF_OnItemChange;
        this._workflowAssociationObject.AutoStartCreate = this._paramAutoStartWF_OnItemCreate;
        this._workflowAssociationObject.AllowManual = this._paramAllowManualStart;

        // Serialize the workflow form data, so that we can save it to the association and pass it to the workflow
        this._workflowAssociationObject.AssociationData = serializeAssociationFormDataToXML();

        // Check to see if the Task List or History List has been change or need to be created
        if (this._workflowAssociationObject.TaskListTitle != this._workflowtTaskList.Title)
        {
            // The Task List is new, so we'll save that value and will create it in code later
            this._workflowAssociationObject.SetTaskList(this._workflowtTaskList);
        }

        if (this._workflowAssociationObject.HistoryListTitle != this._workflowHistoryList.Title)
        {
            // The History List is new, so we'll save that value and will create it in code later
            this._workflowAssociationObject.SetHistoryList(this._workflowHistoryList);
        }
    }

    // Code that responds to the Submit Button Click event.  It's where all the "magic" happens.
    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        // Create the History and Task list if necessare
        SetWorkflowHistoryList();
        SetWorkflowTaskList();

        // If the association between this workflow instance and list doesn't exist, then create it
        if (this._worfkflowAssociationExists == true)
        {
            // Now that the association exists, we can set the values of the Worfkow Association Objects values
            setWorkflowToListAssociationValues();

            // Update association that already exists within the SharePoint List 
            // Note: We are using UpdateWorkflowAssociation(), because we are updating and not adding (a few lines below)
            this._sharePointListAttachedTo.UpdateWorkflowAssociation(this._workflowAssociationObject);
        }
        else
        {
            // No association exist, so create a new association with this SharePoint List
            this._workflowAssociationObject = SPWorkflowAssociation.CreateListAssociation(
                this._workflowTemplateObject,     // Represents the Workflow Template Instance
                this._paramWorkflowName,    // Represents the Name of my new workflow instance
                this._workflowtTaskList,     // Represents the task list that I will associate with the new instance
                this._workflowHistoryList); // Represents the history list that I will associate with the new instance

            // Now that the association exists, we can add the values of the Worfkow Parameters
            setWorkflowToListAssociationValues();

            // ADD the association to the SharePoint List
            // Note: We are using AddWorkflowAssociation(), because we are adding and not updating as we did
            //       earlier in our code (a few lines above)
            this._sharePointListAttachedTo.AddWorkflowAssociation(this._workflowAssociationObject);
        }

        // If everything worked, then redirect the user to the Workflow Setup Screen for the list
        SPUtility.Redirect("WrkSetng.aspx?List=" + this._sharePointListAttachedTo.ID,
                                                    SPRedirectFlags.RelativeToLayoutsPage,
                                                    HttpContext.Current);
    }

    // Code that responds to the Cancel Button Click event. 
    protected void btnCancel_Click(object sender, EventArgs e)
    {
        // Just redirect the user back to the Workflow Settings page, so that they can decide if they want to add another
        // workflow or not and don't do anything with the Association data, and don't create any new History/Task Lists
        SPUtility.Redirect("WrkSetng.aspx?List=" + this._sharePointListAttachedTo.ID,
                                            SPRedirectFlags.RelativeToLayoutsPage,
                                            HttpContext.Current);
    }
}

[Serializable]
public class UpdateContentTypeAssociationData
{
    public string urlTFSWS;
    public string errorEmailTo;
}
